//! View types describing what a reader has observed about a queue.
//!
//! This is more useful on the sender side of a queue than the receiver.

use std::ops::Range;

use crate::QueueIdent;

/// Generic reader state of a queue.
#[derive(Clone, Debug)]
pub struct QueueView {
    /// The index that the next message in a queue will have.
    next_idx: u64,

    /// The tail index in the queue.
    tail_idx: u64,

    /// The last index that a reader has consumed.  They can only view messages
    /// after this one.
    ///
    /// If this is `u64::MAX`, signals that this was unset.
    last_consumed_idx: u64,
}

impl QueueView {
    /// Creates a new queue view, with a raw `last_consumed_idx`.
    pub fn new(next_idx: u64, tail_idx: u64, last_consumed_idx: u64) -> Self {
        Self {
            next_idx,
            tail_idx,
            last_consumed_idx,
        }
    }

    pub fn next_idx(&self) -> u64 {
        self.next_idx
    }

    pub fn tail_idx(&self) -> u64 {
        self.tail_idx
    }

    pub fn last_consumed_idx(&self) -> Option<u64> {
        if self.last_consumed_idx == u64::MAX {
            None
        } else {
            Some(self.last_consumed_idx)
        }
    }

    /// Returns if the queue is empty.
    pub fn is_empty(&self) -> bool {
        self.next_idx() == self.tail_idx()
    }
}

/// Describes a range of messages in a fetch request.
#[derive(Clone, Debug)]
pub struct MsgRange {
    start_idx: u64,
    count: u32,
}

impl MsgRange {
    pub fn new(start_idx: u64, count: u32) -> Self {
        Self { start_idx, count }
    }

    /// Returns an iterator over the message range.
    pub fn iter(&self) -> Range<u64> {
        self.start_idx..(self.start_idx + self.count as u64)
    }
}

/// Describes a list of queues from the same namespace and the last messages in each queue.
///
/// Should be sorted, but is not actually required.
#[derive(Clone, Debug)]
pub struct QueueList {
    entries: Vec<QueueListEntry>,
}

impl QueueList {
    pub fn new(mut entries: Vec<QueueListEntry>) -> Self {
        sort_entry_list(&mut entries);
        Self { entries }
    }

    pub fn entries(&self) -> &[QueueListEntry] {
        &self.entries
    }

    pub fn into_entries(self) -> Vec<QueueListEntry> {
        self.entries
    }

    /// Sorts the list in-place.  This leaves it semantically unchanged.
    pub fn sort(&mut self) {
        sort_entry_list(&mut self.entries);
    }

    fn find_max_ident(&self) -> Option<&QueueIdent> {
        self.entries
            .iter()
            .max_by_key(|e| e.ident)
            .map(|e| &e.ident)
    }

    /// Returns the selector for the "next" ident we should start our query for.
    ///
    /// This handles empty lists and unsorted lists.
    pub fn next_query_selector_ident(&self) -> QueueIdent {
        self.find_max_ident()
            .map(|id| id.next_ident())
            .unwrap_or_else(QueueIdent::zero)
    }
}

fn sort_entry_list(list: &mut [QueueListEntry]) {
    list.sort_by_key(|e| e.ident)
}

/// An entry in a queue list.
///
/// This includes the index of the last message, so that we can compare it
/// against our last known index for the queue and decide easily if we need to
/// fetch from it.
#[derive(Clone, Debug)]
pub struct QueueListEntry {
    ident: QueueIdent,
    last_msg: u64,
}

impl QueueListEntry {
    pub fn new(ident: QueueIdent, last_msg: u64) -> Self {
        Self { ident, last_msg }
    }

    pub fn ident(&self) -> &QueueIdent {
        &self.ident
    }

    pub fn last_msg(&self) -> u64 {
        self.last_msg
    }
}
